Se utilizará los datos de censo para 8 condados centrales del estado de New York
ny <- st_read("data/NY8_utm18.shp")
## Reading layer `NY8_utm18' from data source
## `C:\Users\Usuario\Documents\Universidad\2022-I\Espacial\ArealData\data\NY8_utm18.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 281 features and 17 fields
## Geometry type: POLYGON
## Dimension: XY
## Bounding box: xmin: 358241.9 ymin: 4649755 xmax: 480393.1 ymax: 4808545
## Projected CRS: WGS 84 / UTM zone 18N
plot(st_geometry(ny), axes = T)
La creación de pesos espaciales es un paso necesario en el análisis de datos en área. El primer paso es definir cuales relaciones entre las observaciones tendrán pesos no nulos, es decir, escoger el criterio para que dos enlaces sean considerados como “vecinos”; el segundo es asignar los pesos a los enlaces identificados como “vecinos” en el paso anterior.
Crear los vecinos y pesos no es un proceso fácil y por lo tanto
existen varias funciones y métodos en el paquete spdep que
ayudan a realizar este trabajo. El paquete ade4 contiene
las funciones nb2neig() y neig2nb() las cuales
ayudan a transformar objetos entre las clases nb y
neig.
En el paquete spdep las observaciones enlazadas
(vecinas) son objetos de clase nb; dichos objetos pueden
ser vistos como una lista de tamaño \(n\) donde cada elemento contiene un vector
con los indices de sus correspondientes vecinos. Si alguna observación
no tiene vecinos, la entrada correspondiente contiene el valor cero (0).
Estos objetos también contiene otro tipo de atributos como lo puede ser
un vector de caracteres para identificar cada una de las regiones
consideradas, además de un vector lógico (booleano) que indica si una
relación es simétrica o no (se discute más adelante). La función
card() devuelve el número de vecinos para cada elemento de
la lista (objeto nb). A continuación se presenta brevemente
la estructura de estos objetos espaciales:
NY_nb <- read.gal("data/NY_nb.gal", # read.gal carga el archivo de clase nb
region.id = as.numeric(row.names(ny)) - 1
) # Debe empezar en 0
summary(NY_nb)
## Neighbour list object:
## Number of regions: 281
## Number of nonzero links: 1522
## Percentage nonzero weights: 1.927534
## Average number of links: 5.41637
## Link number distribution:
##
## 1 2 3 4 5 6 7 8 9 10 11
## 6 11 28 45 59 49 45 23 10 3 2
## 6 least connected regions:
## 55 97 100 101 244 245 with 1 link
## 2 most connected regions:
## 34 82 with 11 links
plot(st_geometry(ny), border = "grey60", axes = T)
plot(NY_nb, st_geometry(ny), pch = 19, cex = 0.6, add = T)
Un archivo gal es una matriz de pesos o vecinos
producida por el software GeoDa. Para esta situación particular, todas
las regiones contiguas son consideradas como regiones vecinas.
Luego de ilustrar los datos espaciales que van a ser tratados, se
usará un subconjunto de ellos para mostrar el uso de las principales
funciones y métodos para los objetos nb; el subconjunto a
usar corresponde a Syracuse city.
Syracuse <- filter(ny, AREANAME == "Syracuse city")
Sy0_nb <- subset(NY_nb, ny$AREANAME == "Syracuse city")
plot(st_geometry(Syracuse), border = "grey60", axes = T)
plot(Sy0_nb, st_geometry(Syracuse), pch = 19, cex = 0.6, add = T)
Los principales métodos de los objetos nb son
print(), summary() y plot(), sin
embargo, no son los únicos que existen.
Tanto print como summary presentan el
número de regiones, enlaces no nulos, porcentaje de pesos no nulos y
promedio de enlaces entre regiones, sin embargo, summary
presenta adicionalmente la distribución del número de enlaces del objeto
nb, además de las regiones con menor y mayor frecuencia de
enlaces dentro del objeto.
summary(Sy0_nb)
## Neighbour list object:
## Number of regions: 63
## Number of nonzero links: 346
## Percentage nonzero weights: 8.717561
## Average number of links: 5.492063
## Link number distribution:
##
## 1 2 3 4 5 6 7 8 9
## 1 1 5 9 14 17 9 6 1
## 1 least connected region:
## 164 with 1 link
## 1 most connected region:
## 136 with 9 links
Adicionalmente, el método summary también reporta la
presencia o no de asimetría en el objeto nb
La asimetria está presente cuando i es un vecino de j, pero j no es un vecino de i.
El resultados del método summary brinda la siguiente
información:
Se observa también la distribución de las relaciones que indican que el número máximo de vecinos que se encontraron fueron 9 y estuvieron con respecto a un poligono, 17 de las 63 regiones presentes tienen 6 uniones, etc.
La función card permite obtener de forma rápida el
número de vecinos de cada región.
card(Sy0_nb)
## [1] 5 5 2 7 7 5 4 5 7 5 5 7 5 7 6 6 6 6 6 4 4 7 6 8 4 3 3 9 6 8 6 6 6 8 6 4 4 8
## [39] 6 6 7 5 7 6 4 5 3 5 6 4 6 7 4 8 5 1 5 8 5 5 6 3 3
Se crearán objetos de lista de vecinos para uso posterior en el
documento, para esto se utiliza la función knearneigh para
obtener el índice de los puntos de \(k\) vecinos cercanos, esta función recibe
como parametro de ingreso la lista de coordenadas de los puntos y un
valor \(k\) que indica el número de
puntos vecinos cercanos que se desean retornar. El resultado de la
operación anterior se ingresa a la función knn2nb para
convertir el objeto resultante en una lista de vecinos de la clase
nb.
coords <- st_point_on_surface(st_geometry(Syracuse))
ids <- row.names(Syracuse)
Sy8_nb <- knn2nb(knearneigh(coords, k = 1), row.names = ids)
Sy8_nb
## Neighbour list object:
## Number of regions: 63
## Number of nonzero links: 63
## Percentage nonzero weights: 1.587302
## Average number of links: 1
## Non-symmetric neighbours list
la función nbdists obtiene las distancias entre los
puntos de los objectos vecinos calculados en el paso anterior. Esto es
util porque con ella se obtiene la distancia minima necesaria
para asegurar que todas las áreas tengan al menos un
vecino.
Posteriormente se utiliza la función dnearneigh para
obtener la lista de objetos vecinos (nb) que se encuentran a una
determinada distancia (d1, d2).
dsts <- unlist(nbdists(Sy8_nb, coords))
Sy11_nb <- dnearneigh(x = coords, d1 = 0, d2 = 0.75 * max(dsts))
Sy11_nb
## Neighbour list object:
## Number of regions: 63
## Number of nonzero links: 156
## Percentage nonzero weights: 3.930461
## Average number of links: 2.47619
## 8 regions with no links:
## 1 10 20 36 46 57 60 61
Como resultado, se observa que existen 8 regiones que no poseen vecinos (1, 10, 20…).
La literatura respecto a los pesos espaciales es muy pequeña, lo cual resulta sorprendente teniendo en cuenta la gran importancia que tienen los pesos espaciales al momento de medir y modelar dependencia espacial para datos en área.
Los pesos espaciales se pueden ver como una lista de pesos indexados por una lista de vecinos, donde el peso de la relación entre \(i\) e \(j\) es el \(k\)-ésimo elemento del \(i\)-ésimo componente de la lista de pesos.
Una vez definida la lista de vecinos espaciales, el próximo paso es asignar pesos espaciales a cada uno de los enlaces entre regiones. En caso de conocer características importante del proceso espacial que se este tratando, dicha información puede ser útil al momento de asignarle pesos a los enlaces, de lo contrario es mejor evitarlo para evadir problemas de mala especificación del mismo.
La función nb2listw toma un objeto de lista de vecinos
(nb) y lo convierte en un objeto de pesos. La conversión
por defecto es W, donde los pesos varían entre la unidad
dividido por el más largo y el más pequeño número de vecinos y la suma
de los pesos para cada entidad de área es la unidad.
Sy0_lw_w <- nb2listw(neighbours = Sy0_nb)
Sy0_lw_w
## Characteristics of weights list object:
## Neighbour list object:
## Number of regions: 63
## Number of nonzero links: 346
## Percentage nonzero weights: 8.717561
## Average number of links: 5.492063
##
## Weights style: W
## Weights constants summary:
## n nn S0 S1 S2
## W 63 3969 63 24.78291 258.564
Otro tipo de conversión se obtiene al configurar style = B, con lo cual se obtiene un peso de 1 por cada relación vecina y 0 en otro caso, pero en esta situación, la suma de los pesos para áreas difieren de acuerdo al número de vecinos que las áreas tengan.
Se recomienda utilizar pesos binarios cuando se tiene poco conocimiento acerca del proceso que se está estudiando.
El argumento glist puede ser usado para asignar manualmente
los pesos a cada enlace entre regiones del objeto nb; como
se mencionó previamente, esto puede ser útil en situaciones donde se
tiene información respecto a las posibles relaciones que pueden existir
entre dos regiones, lo cual podría ayudar a obtener mejores resultados.
A continuación se presenta un ejemplo suponiendo que los pesos obedecen
a la regla IDW binaria (inverso de las distancias entre regiones)
dsts <- nbdists(Sy0_nb, coords)
idw <- lapply(dsts, function(x) 1 / (x / 1000))
Sy0_lw_idwB <- nb2listw(Sy0_nb, glist = idw, style = "B")
summary(unlist(Sy0_lw_idwB$weights))
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.3062 0.7481 0.9547 1.0154 1.2232 2.7686
summary(sapply(Sy0_lw_idwB$weights, sum))
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.321 4.129 5.896 5.577 6.967 9.860
A continuación se presentan 3 tipos de pesos espaciales:
Sy0_lw_B <- nb2listw(Sy0_nb, style = "B")
pal <- brewer.pal(9, "Reds")
oopar <- par(mfrow = c(1, 3), mar = c(1, 1, 3, 1) + 0.1)
z <- t(listw2mat(Sy0_lw_w))
brks <- c(0, 0.1, 0.143, 0.167, 0.2, 0.5, 1)
nbr3 <- length(brks) - 3
image(1:63, 1:63, z[, ncol(z):1],
breaks = brks,
col = pal[c(1, (9 - nbr3):9)],
main = "W style", axes = FALSE
)
box()
z <- t(listw2mat(Sy0_lw_B))
image(1:63, 1:63, z[, ncol(z):1],
col = pal[c(1, 9)], main = "B style",
axes = FALSE
)
box()
z <- t(listw2mat(Sy0_lw_idwB))
brks <- c(0, 0.35, 0.73, 0.93, 1.2, 2.6)
nbr3 <- length(brks) - 3
image(1:63, 1:63, z[, ncol(z):1],
breaks = brks,
col = pal[c(1, (9 - nbr3):9)],
main = "IDW B style", axes = FALSE
)
box()
par(oopar)
El último argumento a considerar en la función
nb2listw() es zero.policy. Cuando existen regiones
sin vecinos es necesario especificar el argumento
zero.policy = T para que no arroje error, esto debido a que
por defecto la función nb2listw() asume que cada región
tiene al menos un vecino.
try(
expr = {
nb2listw(neighbours = Sy11_nb, style = "B", zero.policy = F)
}
)
## Error in nb2listw(neighbours = Sy11_nb, style = "B", zero.policy = F) :
## Empty neighbour sets found
Luego de especificar como verdadero dicho argumento, se obtiene el resultado sin fallas computacionales
Sy0_lw_D1 <- nb2listw(neighbours = Sy11_nb, style = "B", zero.policy = T)
print(Sy0_lw_D1, zero.policy = T)
## Characteristics of weights list object:
## Neighbour list object:
## Number of regions: 63
## Number of nonzero links: 156
## Percentage nonzero weights: 3.930461
## Average number of links: 2.47619
## 8 regions with no links:
## 1 10 20 36 46 57 60 61
##
## Weights style: B
## Weights constants summary:
## n nn S0 S1 S2
## B 55 3025 156 312 2240
Hay dos tipos de test de autocorrelación espacial que se aplican a este tipo de datos:
Es la forma más común de abordar la autocorrelación espacial. Se calcula como la razón del producto de la variable de interés y su rezago espacial, con el producto cruzado de la variable de interés y ajustada por los pesos espaciales usados.
\[I = \frac{n}{\sum_{i=1}^{n} \sum_{j=1}^{n} w_{ij}}\ \frac{\sum_{i=1}^{n} \sum_{j=1}^{n} (y_i - \bar{y})(y_j - \bar{y})}{\sum_{i=1}^{n} (y_i - \bar{y})^2}\] donde:
Centrarse en la media es equivalente a afirmar que el modelo correcto tiene una media constante, y que cualquier patrón restante después del centrado es causado por las relaciones espaciales codificadas en los pesos espaciales.
Para esto, se calcula la esperanza como:
\[E(I) = \frac{-1}{n-1}\]
El siguiente paso es tomar el valor observado y restarlo al valor esperado analíticamente (I - E(I)) para finalmente dividirlo por la raíz cuadrada de la varianza analítica. El resultado es comparado con la distribución normal para encontrar el valor de probabilidad del estadístico observado bajo la hipótesis nula de ausencia de dependencia espacial para los pesos espaciales elegidos. Los resultados pueden depender de la elección de las ponderaciones, de los vecindarios y de que los supuestos sean cumplidos.
\[H_0: Ausencia \ de \ dependencia \ espacial\] \[H_1: El \ estadístico \ observado \ es \ significativamente \ más \ grande \ que \ el \ valor \ esperado\]
El dominio del I de Moran es [-1, 1]. Se evidencia autocorrelación espacial positiva cuando I > 0. Esto quiere decir que las unidades de análisis vecinas tienden a ser similares.
moran.test(x = ny$Cases, listw = nb2listw(NY_nb))
##
## Moran I test under randomisation
##
## data: ny$Cases
## weights: nb2listw(NY_nb)
##
## Moran I statistic standard deviate = 3.9778, p-value = 3.477e-05
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic Expectation Variance
## 0.146882990 -0.003571429 0.001430595
Por defecto, el moran.test usa el supuesto de
aleatorización lo que difiere del supuesto de normalidad más simple al
introducir un término de corrección basado en la curtosis de la variable
de interés. Cuando la curtosis corresponde a la de una variable con
distribución normal, las dos suposiciones arrojan la misma varianza,
pero a medida que la variable se aleja de la normalidad, la suposición
de aleatorización compensa aumentando la varianza y disminuyendo la
desviación estándar.
moran.test(x = ny$Cases, listw = nb2listw(NY_nb), randomisation = F)
##
## Moran I test under normality
##
## data: ny$Cases
## weights: nb2listw(NY_nb)
##
## Moran I statistic standard deviate = 3.9731, p-value = 3.547e-05
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic Expectation Variance
## 0.146882990 -0.003571429 0.001433975
En este caso, se observa que la varianza no cambia, por lo tanto, el supuesto de normalidad se cumple.
Es posible también realizar test de Monte Carlo para evaluar la hipótesis de dependencia espacial con el test de Moran I.
moran.mc(x = ny$Cases, listw = nb2listw(NY_nb), nsim = 999)
##
## Monte-Carlo simulation of Moran I
##
## data: ny$Cases
## weights: nb2listw(NY_nb)
## number of simulations + 1: 1000
##
## statistic = 0.14688, observed rank = 1000, p-value = 0.001
## alternative hypothesis: greater
Conociendo las medidas globales de asociación espacial, es posible descomponer estas medidas con el fin de construir test locales que buscan detectar clusters (Observaciones con vecinos muy similares) y hotspots (Observaciones con vecinos muy diferentes), además se pueden utilizar para conocer el impacto de los estadísticos individuales en sus contrapartes globales.
(Anselin, 1995)
Cualquier estadístico que cumplas las siguientes condiciones:
Comprobar si la contribución local a la agrupación en torno a la observación i es significativamente diferente de 0, identificando localidades donde existe un grado significativo de agrupación espacial.
Partiendo del índice global de Moran:
\[ I = \frac{n}{\sum_{i=1}^{n} \sum_{j=1}^{n} w_{ij}}\ \frac{\sum_{i=1}^{n} \sum_{j=1}^{n} (y_i - \bar{y})(y_j - \bar{y})}{\sum_{i=1}^{n} (y_i - \bar{y})^2} \]
Se reescribe le ecuación en:
\[ I = n \left[ \sum_k \sum_j w_{kj} \right]^{-1}\left[ \sum_k (y_k-\bar{y})^2 \right]^{-1} \sum_i I_i \]
donde:
\[ I_i = (y_i-\bar{y}) \sum_j w_{ij} (y_j - \bar{y}) \]
Como:
\(n \left[ \sum_k \sum_j w_{kj} \right]^{-1}\left[ \sum_k (y_k-\bar{y})^2 \right]^{-1}\) no depende de i, entonces para un conjunto dado de \(z_i\) esta expresión se comporta como una constante.
\[ I = cte \cdot \sum_i I_i \]
Reemplazando \(q_i = y_i - \bar{y}\) se obtiene la expresión original propuesta por Anselin, 1995.
\[ I_i = z_i \sum_j w_{ij}z_j \]
\(I_i\) es el producto de \(z_i\) y la media de los valores \(z_j\) de los vecinos del polígono \(i\)
Finalmente, para cada \(I_i\) se puede aplicar un test de significancia estadística con una hipótesis nula de ausencia de asociación espacial.
Cuadrantes de asociación espacial. Modificado de Siabato et al., 2018
El gráfico de dispersión de Moran por convención posiciona la variable de interés en el eje x y los valores de rezago espacial (La suma de valores de los vecinos espacialmente ponderada) en el eje y. El índice global de Moran es una relación lineal entre estos valores y es dibujado como la pendiente.
(ggplot(moran_plot, aes(x, wx)) +
geom_point(aes(shape = is_inf, color = is_inf), size = 0.75) +
scale_color_manual(values = c("black", "red")) +
geom_hline(
yintercept = moran_plot$wx %>% mean(),
linetype = "dashed", alpha = 0.5
) +
geom_vline(
xintercept = moran_plot$x %>% mean(),
linetype = "dashed", alpha = 0.5
) +
geom_text(aes(x, wx, label = labels),
data = filter(moran_plot, is_inf),
size = 2.5
) +
geom_smooth(method = "lm", se = F, size = .6, col = "black") +
labs(
x = "Cases", y = "Spatial lagged cases",
title = "Moran Scatterplot",
shape = "Significativo", color = "Significativo"
) +
theme_bw() +
theme(plot.title = element_text(hjust = 0.5))) %>%
plotly::ggplotly()
El gráfico se divide en 4 cuadrantes. El cuadrante de la esquina superior derecha indica áreas que tienen un alta incidencia de casos de leucemia y están rodeadas por otras áreas que tienen nivel superior al promedio de casos (high-high). En la esquina inferior izquierda se encuentran áreas con baja incidencia de casos de leucemia rodeadas por áreas con incidencia de leucemia por debajo del promedio (low-low). Las áreas high-high son conocidas como hotspots y las áreas low-low como coldspots. En la diagonal opuesta se tienen valores atípicos espaciales, que indica que son áreas que están rodeadas por vecinos muy diferentes a ellos.
Al igual que en el caso de la estadística global, se puede comprobar la divergencia de los estadísticos locales de los valores esperados, bajo los supuestos de normalidad y aleatoriedad analíticamente y usando métodos como el de punto de silla y exacto.
local <- as.data.frame(localmoran(ny$Cases,
listw = nb2listw(NY_nb, style = "C")
))
local2 <- as.data.frame(localmoran.sad(lm(Cases ~ 1, ny),
nb = NY_nb, style = "C"
))
local3 <- as.data.frame(localmoran.exact(lm(Cases ~ 1, ny), nb = NY_nb, style = "C"))
ny %>%
select(Cases) %>%
mutate(
p_value = moran_plot$is_inf,
rezago = lag.listw(nb2listw(NY_nb, style = "B"), Cases),
cuadrante = case_when(
(Cases > mean(Cases)) & (rezago > mean(rezago)) &
(p_value == T) ~ "High-High",
(Cases <= mean(Cases)) & (rezago <= mean(rezago)) &
(p_value == T) ~ "Low-Low",
(Cases > mean(Cases)) & (rezago <= mean(rezago)) &
(p_value == T) ~ "High-Low",
(Cases <= mean(Cases)) & (rezago > mean(rezago)) &
(p_value == T) ~ "Low-High",
TRUE ~ "NS"
),
index = moran_plot$labels
) -> resultados
resultados_coords <- filter(resultados, p_value == T) %>%
mutate(
x = (st_centroid(resultados$geometry) %>% st_coordinates())[1],
y = (st_centroid(resultados$geometry) %>% st_coordinates())[2]
)
(ggplot(resultados) +
geom_sf(aes(fill = cuadrante), alpha = 0.8) +
scale_fill_manual(values = viridisLite::viridis(5)) +
labs(
title = "Lugares con influencia de Leucemia \n",
x = "Longitud", y = "Latitud"
) +
geom_sf_text(
data = resultados_coords,
mapping = aes(x, y, label = index),
color = "white"
) +
theme_bw() +
theme(
plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
axis.text = element_blank(),
axis.ticks = element_blank()
)) %>%
plotly::ggplotly()
Se puede calcular una evaluación de riesgo constante asumiendo una distribución Poisson con un valor de \(\lambda\) igual a la probabilidad de ocurrencia de la enfermedad, se estandariza la variable tomando la propiedad de que tanto la media como la desviación estandar son iguales en esta distribución, se calculan los rezagos y se compara el indice de morán actual con el de riesgo constante.
ny %>%
transmute(
p_afectada = sum(Cases) / sum(POP8) * POP8,
casos_std = (Cases - p_afectada) / sqrt(p_afectada),
rezago = stats::lag(nb2listw(NY_nb, style = "C"), casos_std),
indice_riesgo = casos_std * rezago,
indice = local$Ii
) -> riesgo
m_riesgo <- ggplot(riesgo) +
geom_sf(aes(fill = indice_riesgo)) +
labs(title = "Mapa de riesgo Constante") +
scale_fill_gradientn(
colours = viridisLite::viridis(20,
begin = 0.17,
end = 0.98
),
limits = c(-2.2, 3.7)
) +
theme_bw() +
theme(
plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
axis.text = element_blank(),
axis.ticks = element_blank()
)
m_actual <- ggplot(riesgo) +
geom_sf(aes(fill = indice)) +
labs(title = "Mapa de riesgo Standard") +
scale_fill_gradientn(
colours = viridisLite::viridis(20,
begin = 0.17,
end = 0.98
),
limits = c(-2.2, 3.7)
) +
theme_bw() +
theme(
plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
axis.text = element_blank(),
axis.ticks = element_blank()
)
cowplot::plot_grid(m_actual, m_riesgo, align = "hv", ncol = 2)
# set.seed(1234)
# nsim <- 999
# N <- length(rni)
# sims <- matrix(0, ncol = nsim, nrow = N)
# for (i in 1:nsim) {
# y <- rpois(N, lambda = rni)
# sdCRi <- (y - rni)/sqrt(rni)
# wsdCRi <- lag(lw, sdCRi)
# sims[, i] <- sdCRi * wsdCRi
# }
# xrank <- apply(cbind(I_CR, sims), 1, function(x) rank(x)[1])
# diff <- nsim - xrank
# diff <- ifelse(diff > 0, diff, 0)
# pval <- punif((diff + 1)/(nsim + 1))
monte_carlo_sim <- function(nsim, prop, Ii, nb) {
N <- length(prop)
sims <- matrix(0, ncol = nsim, nrow = N)
for (i in 1:nsim) {
y <- rpois(N, lambda = prop)
z <- (y - prop) / sqrt(prop)
rezago <- stats::lag(nb2listw(nb, style = "C"), z)
sims[, i] <- z * rezago
}
xrank <- apply(cbind(Ii, sims), 1, function(x) rank(x)[1])
diff <- nsim - xrank
diff <- ifelse(diff > 0, diff, 0)
pval <- punif((diff + 1) / (nsim + 1))
return(pval)
}
mc_sim <- riesgo %>%
select(p_afectada, indice_riesgo) %>%
mutate(
pvalue_cr = monte_carlo_sim(
999, p_afectada,
indice_riesgo, NY_nb
),
pvalue_sad = local2$`Pr. (Sad)`,
pvalue_exact = local3$`Pr. (exact)`,
pvalue_rand = local$`Pr(z != E(Ii))`,
pvalue_norm = local2$`Pr. (N)`
) %>%
pivot_longer(
cols = c(
"pvalue_cr", "pvalue_sad",
"pvalue_exact", "pvalue_rand", "pvalue_norm"
),
names_to = "type", values_to = "p_value"
) %>%
mutate(discrete = cut(p_value,
breaks = c(
0, 0.05, 0.1,
0.9, 0.95, 1, max(p_value)
),
include.lowest = T
))
aux_labs <- c(
"pvalue_cr" = "Riesgo constante",
"pvalue_exact" = "Exacto",
"pvalue_norm" = "Normal",
"pvalue_rand" = "Aleatorio",
"pvalue_sad" = "Saddlepoint"
)
ggplot(mc_sim) +
geom_sf() +
geom_sf(aes(fill = discrete),
data = filter(mc_sim, p_value < 0.1 | p_value > 0.9 &
p_value < 1)
) +
facet_wrap(~type, labeller = labeller(type = aux_labs)) +
labs(title = "") +
scale_fill_manual(values = viridisLite::viridis(5), name = "P-value") +
theme_bw() +
theme(
plot.title = element_text(size = 14, face = "bold", hjust = 0.5),
axis.text = element_blank(),
axis.ticks = element_blank()
)